<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Pen Tools</title>
    <script src="Point.js"></script>
    <script src="PointPath.js"></script>
    <script src="CompoundPath.js"></script>
</head>
<body>
<h3>This is the pen tools test</h3>
<canvas id="canvas" width="1200" height="600" style="border: 1px solid chocolate"></canvas>
<script type="text/javascript">

    var path = new PointPath();
    var cp = new CompoundPath();
    var RADIUS = 4;
    var START = false;
    var MOVE = false;// Determine whether the anchor point moves or note
    var LEN = 0;// Determine where is the penultimate point

    canvas.addEventListener("mousedown", mouseDown, false);
    canvas.addEventListener("mousemove", mouseMove, false);
    canvas.addEventListener("mouseup", mouseUp, false);
    var ctx = document.getElementById("canvas").getContext("2d");

    function mouseDown(e) {
        var anchorPoint = new Point();// An anchor point
        anchorPoint.x = e.offsetX;
        anchorPoint.y = e.offsetY;
        anchorPoint.type = Point.prototype.POD_TT_PRIM_LINE;

        if (path.points.length < 1) {
            anchorPoint.type = Point.prototype.POD_TT_PRIM_MOVE;
        }
        path.points.push(anchorPoint);

        if(path.points.length > 1){
            if(path.points[path.points.length - 2].type == Point.prototype.POD_TT_PRIM_MOVE ||
                    path.points[path.points.length - 2].type == Point.prototype.POD_TT_PRIM_LINE ||
                    path.points[path.points.length - 2].type == Point.prototype.POD_TT_PRIM_CSPLINE &&
                    path.points[path.points.length - 2].x == path.points[path.points.length - 3].x &&
                    path.points[path.points.length - 2].y == path.points[path.points.length - 3].y){
                LEN = path.points.length - 2;
            }
            else {
                LEN = path.points.length - 3;
            }
        }
        console.log("length: " + path.points.length + " LEN: " + LEN);
        drawBegin();
        START = true;
    }

    function drawBegin() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.beginPath();
        ctx.arc(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y, RADIUS, 0, Math.PI * 2, true);
        ctx.strokeStyle = "blue";
        ctx.stroke();

        ctx.strokeStyle="black";
        drawBezier();

        if(LEN == path.points.length - 2){
            ctx.beginPath();
            ctx.arc(path.points[LEN].x, path.points[LEN].y, RADIUS, 0, Math.PI*2);
            ctx.strokeStyle = "blue";
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y);
            ctx.lineTo(path.points[path.points.length - 2].x, path.points[path.points.length - 2].y);
            ctx.stroke();
        }
        else if(LEN == path.points.length - 3){
            ctx.beginPath();
            ctx.arc(path.points[LEN].x, path.points[LEN].y, RADIUS, 0, Math.PI*2);
            ctx.strokeStyle = "blue";
            ctx.stroke();

            ctx.beginPath();
            ctx.arc(path.points[LEN + 1].x, path.points[LEN + 1].y, RADIUS, 0, Math.PI*2);
            ctx.fillStyle = "blue";
            ctx.fill();

            ctx.beginPath();
            ctx.moveTo(path.points[LEN].x, path.points[LEN].y);
            ctx.lineTo(path.points[LEN + 1].x, path.points[LEN + 1].y);
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(path.points[LEN].x, path.points[LEN].y);
            ctx.bezierCurveTo(path.points[LEN + 1].x, path.points[LEN + 1].y,
                    path.points[path.points.length - 1].x, path.points[path.points.length - 1].y,
                    path.points[path.points.length - 1].x, path.points[path.points.length - 1].y);
            ctx.stroke();
        }

        ctx.beginPath();
        ctx.arc(path.points[LEN].x, path.points[LEN].y, RADIUS - 1, 0, Math.PI*2);
        ctx.fillStyle = "white";
        ctx.fill();

        ctx.beginPath();
        ctx.arc(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y, RADIUS - 1, 0, Math.PI*2);
        ctx.fill();
    }

    function mouseMove(e) {
        if (START) {
            var pOut = new Point();// A point in the line of outward direction
            pOut.x = e.offsetX;
            pOut.y = e.offsetY;
            var pIn = new Point();// A point in the line of inward direction
            pIn.x = path.points[path.points.length - 1].x - (pOut.x - path.points[path.points.length - 1].x);
            pIn.y = path.points[path.points.length - 1].y - (pOut.y - path.points[path.points.length - 1].y);
            MOVE = true;
            drawMove(pOut.x, pOut.y, pIn.x, pIn.y);
        }
    }

    function drawMove(pout_x, pout_y, pin_x, pin_y) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        ctx.beginPath();
        ctx.arc(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y, RADIUS, 0, Math.PI * 2, true);
        ctx.strokeStyle = "blue";
        ctx.stroke();

        ctx.beginPath();
        ctx.arc(pout_x, pout_y, RADIUS, 0, Math.PI * 2, true);
        ctx.fillStyle = "blue";
        ctx.fill();

        ctx.beginPath();
        ctx.arc(pin_x, pin_y, RADIUS, 0, Math.PI * 2, true);
        ctx.fill();

        ctx.beginPath();
        ctx.moveTo(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y);
        ctx.lineTo(pout_x, pout_y);
        ctx.stroke();

        ctx.beginPath();
        ctx.moveTo(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y);
        ctx.lineTo(pin_x, pin_y);
        ctx.stroke();

        ctx.strokeStyle="black";
        drawBezier();

        if(LEN == path.points.length - 2){
            ctx.beginPath();
            ctx.arc(path.points[LEN].x, path.points[LEN].y, RADIUS, 0, Math.PI*2);
            ctx.strokeStyle = "blue";
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(path.points[LEN].x, path.points[LEN].y);
            ctx.bezierCurveTo(path.points[LEN].x, path.points[LEN].y, pin_x, pin_y,
                    path.points[path.points.length - 1].x, path.points[path.points.length - 1].y);
            ctx.stroke();
        }
        else if(LEN == path.points.length - 3){
            ctx.beginPath();
            ctx.arc(path.points[LEN].x, path.points[LEN].y, RADIUS, 0, Math.PI*2);
            ctx.strokeStyle = "blue";
            ctx.stroke();

            ctx.beginPath();
            ctx.arc(path.points[LEN + 1].x, path.points[LEN + 1].y, RADIUS, 0, Math.PI*2);
            ctx.fillStyle = "blue";
            ctx.fill();

            ctx.beginPath();
            ctx.moveTo(path.points[LEN].x, path.points[LEN].y);
            ctx.lineTo(path.points[LEN + 1].x, path.points[LEN + 1].y);
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(path.points[LEN].x, path.points[LEN].y);
            ctx.bezierCurveTo(path.points[LEN + 1].x, path.points[LEN + 1].y, pin_x, pin_y,
                    path.points[path.points.length - 1].x, path.points[path.points.length - 1].y);
            ctx.stroke();
        }
        ctx.beginPath();
        ctx.arc(path.points[LEN].x, path.points[LEN].y, RADIUS - 1, 0, Math.PI*2);
        ctx.fillStyle = "white";
        ctx.fill();

        ctx.beginPath();
        ctx.arc(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y, RADIUS - 1, 0, Math.PI*2);
        ctx.fill();
    }

    function mouseUp(e) {
        START = false;
        var move = false;

        if (MOVE) {
            var pOut = new Point();
            move = true;

            pOut.x = e.offsetX;
            pOut.y = e.offsetY;
            pOut.type = Point.prototype.POD_TT_PRIM_CSPLINE;

            if (path.points.length > 1) {
                var pIn = new Point();
                pIn.x = path.points[path.points.length - 1].x - (pOut.x - path.points[path.points.length - 1].x);
                pIn.y = path.points[path.points.length - 1].y - (pOut.y - path.points[path.points.length - 1].y);
                pIn.type = Point.prototype.POD_TT_PRIM_CSPLINE;

                path.points[path.points.length - 1].type = Point.prototype.POD_TT_PRIM_CSPLINE;// Switch type of the anchor point

                if (path.points[path.points.length - 2].type == Point.prototype.POD_TT_PRIM_LINE ||
                        path.points[path.points.length - 2].type == Point.prototype.POD_TT_PRIM_MOVE ||
                        path.points[path.points.length - 2].type == Point.prototype.POD_TT_PRIM_CSPLINE &&
                        path.points[path.points.length - 2].x == path.points[path.points.length - 3].x &&
                        path.points[path.points.length - 2].y == path.points[path.points.length - 3].y) {
                    var formerOut = new Point();// The point on the tangent line of outward direction of the former point
                    formerOut.x = path.points[path.points.length - 2].x;
                    formerOut.y = path.points[path.points.length - 2].y;
                    formerOut.type = Point.prototype.POD_TT_PRIM_CSPLINE;
                    path.points.splice(path.points.length - 1, 0, formerOut);
                }
                path.points.splice(path.points.length - 1, 0, pIn);
                path.points.push(pOut);
            }
            else {
                path.points.push(pOut);
            }
        }
        else {
            move = false;
            if (path.points.length > 1 && path.points[path.points.length - 2].type == Point.prototype.POD_TT_PRIM_CSPLINE &&
                    (path.points[path.points.length - 2].x != path.points[path.points.length - 3].x ||
                            path.points[path.points.length - 2].y != path.points[path.points.length - 3].y)) {
                var anchorPointIn = new Point();
                anchorPointIn.x = path.points[path.points.length - 1].x;
                anchorPointIn.y = path.points[path.points.length - 1].y;
                anchorPointIn.type = Point.prototype.POD_TT_PRIM_CSPLINE;

                path.points[path.points.length - 1].type = Point.prototype.POD_TT_PRIM_CSPLINE;// Switch type of the anchor point
                path.points.splice(path.points.length - 1, 0, anchorPointIn);
            }
        }
        MOVE = false;

        drawStop(move);
    }

    function drawStop(move) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.strokeStyle="blue";

        if(path.points.length > 2){
            ctx.beginPath();
            if(move){
                LEN = path.points.length - 5;
                ctx.moveTo(path.points[LEN].x, path.points[LEN].y);
                ctx.bezierCurveTo(path.points[LEN + 1].x, path.points[LEN + 1].y, path.points[LEN + 2].x, path.points[LEN + 2].y,
                        path.points[LEN + 3].x, path.points[LEN + 3].y);
                ctx.stroke();
            }
            else {
                if(LEN == path.points.length - 4){
                    ctx.moveTo(path.points[LEN].x, path.points[LEN].y);
                    ctx.bezierCurveTo(path.points[LEN + 1].x, path.points[LEN + 1].y, path.points[LEN + 2].x, path.points[LEN + 2].y,
                            path.points[LEN + 3].x, path.points[LEN + 3].y);
                    ctx.stroke();
                }
                else{
                    ctx.moveTo(path.points[LEN].x, path.points[LEN].y);
                    ctx.lineTo(path.points[LEN + 1].x, path.points[LEN + 1].y);
                    ctx.stroke();
                }
            }
        }
        else if(path.points.length == 2 && !move){
            ctx.moveTo(path.points[0].x, path.points[0].y);
            ctx.lineTo(path.points[1].x, path.points[1].y);
            ctx.stroke();
        }

        drawBezier();

        if(path.points.length > 1) {
            drawPoints();
        }

        if(move){
            ctx.beginPath();
            ctx.arc(path.points[path.points.length - 2].x, path.points[path.points.length - 2].y, RADIUS, 0, Math.PI*2, true);
            ctx.fillStyle = "blue";
            ctx.fill();

            ctx.beginPath();
            ctx.arc(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y, RADIUS, 0, Math.PI*2, true);
            ctx.fill();

            ctx.beginPath();
            ctx.moveTo(path.points[path.points.length - 2].x, path.points[path.points.length - 2].y);
            ctx.lineTo(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y);
            ctx.stroke();

            if(path.points.length > 2){
                ctx.beginPath();
                ctx.arc(path.points[path.points.length - 3].x, path.points[path.points.length - 3].y, RADIUS, 0, Math.PI*2, true);
                ctx.fill();

                ctx.beginPath();
                ctx.moveTo(path.points[path.points.length - 2].x, path.points[path.points.length - 2].y);
                ctx.lineTo(path.points[path.points.length - 3].x, path.points[path.points.length - 3].y);
                ctx.stroke();
            }
            else if(path.points.length == 2){
                var startInX, startInY;
                startInX = path.points[0].x - (path.points[1].x - path.points[0].x);
                startInY = path.points[0].y - (path.points[1].y - path.points[0].y);

                ctx.beginPath();
                ctx.arc(startInX, startInY, RADIUS, 0, Math.PI*2, true);
                ctx.fill();

                ctx.beginPath();
                ctx.moveTo(path.points[0].x, path.points[0].y);
                ctx.lineTo(startInX, startInY);
                ctx.stroke();
            }
        }
        else{
            ctx.beginPath();
            ctx.arc(path.points[path.points.length - 1].x, path.points[path.points.length - 1].y, RADIUS, 0, Math.PI*2, true);
            ctx.fillStyle = "blue";
            ctx.fill();
        }


        if(LEN == path.points.length - 5 && (path.points[LEN].x != path.points[LEN + 1].x || path.points[LEN].y != path.points[LEN + 1].y)
                || LEN == path.points.length - 4){
            ctx.beginPath();
            ctx.arc(path.points[LEN + 1].x, path.points[LEN + 1].y, RADIUS, 0, Math.PI*2);
            ctx.fillStyle = "blue";
            ctx.fill();

            ctx.beginPath();
            ctx.moveTo(path.points[LEN].x, path.points[LEN].y);
            ctx.lineTo(path.points[LEN + 1].x, path.points[LEN + 1].y);
            ctx.stroke();
        }

        ctx.beginPath();
        ctx.arc(path.points[LEN].x, path.points[LEN].y, RADIUS - 1, 0, Math.PI*2);
        ctx.fillStyle="white";
        ctx.fill();

    }

    function drawBezier(){
        ctx.beginPath();
        ctx.moveTo(path.points[0].x, path.points[0].y);

        for(var i = 0; i < LEN;){
            if(path.points[i].type == Point.prototype.POD_TT_PRIM_MOVE && path.points[i + 1].type == Point.prototype.POD_TT_PRIM_LINE ||
                    path.points[i].type == Point.prototype.POD_TT_PRIM_LINE && path.points[i + 1].type == Point.prototype.POD_TT_PRIM_LINE ||
                    path.points[i].type == Point.prototype.POD_TT_PRIM_CSPLINE && path.points[i + 1].type == Point.prototype.POD_TT_PRIM_LINE){
                ctx.lineTo(path.points[i + 1].x, path.points[i + 1].y);
                i++;
            }
            else {
                ctx.bezierCurveTo(path.points[i + 1].x, path.points[i + 1].y, path.points[i + 2].x, path.points[i + 2].y,
                        path.points[i + 3].x, path.points[i + 3].y);
                i += 3;
            }
        }
        ctx.stroke();
    }

    function drawPoints() {
        for(var i = 0; i < LEN;){
            ctx.beginPath();
            ctx.arc(path.points[i].x, path.points[i].y, RADIUS, 0, Math.PI*2, true);
            ctx.strokeStyle = "blue";
            ctx.stroke();

            ctx.beginPath();
            ctx.arc(path.points[i].x, path.points[i].y, RADIUS - 1, 0, Math.PI*2, true);
            ctx.fillStyle = "white";
            ctx.fill();

            if(path.points[i].type == Point.prototype.POD_TT_PRIM_MOVE && path.points[i + 1].type == Point.prototype.POD_TT_PRIM_LINE ||
                    path.points[i].type == Point.prototype.POD_TT_PRIM_LINE && path.points[i + 1].type == Point.prototype.POD_TT_PRIM_LINE ||
                    path.points[i].type == Point.prototype.POD_TT_PRIM_CSPLINE && path.points[i + 1].type == Point.prototype.POD_TT_PRIM_LINE){
                i++;
            }
            else {
                i += 3;
            }
        }

        ctx.beginPath();
        ctx.arc(path.points[LEN].x, path.points[LEN].y, RADIUS, 0, Math.PI*2);
        ctx.stroke();
    }

</script>
</body>
</html>